﻿//-----------------------------------------------------------------------------------------------------------
// WBFSSync Project by Omega Frost 
// http://wbfssync.codeplex.com/
//
// WBFSSync is Licensed under the terms of the 
// Microsoft Reciprocal License (Ms-RL)
//-----------------------------------------------------------------------------------------------------------
using System;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using WBFSe3;
using WbfsSync.Core.Operations;
using WbfsSync.Dialogs;

namespace WbfsSync.Core
{
    //-------------------------------------------------------------------------------------------------------
    //
    //-------------------------------------------------------------------------------------------------------
    public enum CoverMode
    {
        CoverSimple     = 0,
        Cover3D         = 1,
        Disc            = 2,
        DiscCustom      = 3,
    }


    //-------------------------------------------------------------------------------------------------------
    //
    //-------------------------------------------------------------------------------------------------------
    public static class Covers
    {
        //---------------------------------------------------------------------------------------------------
        //
        //---------------------------------------------------------------------------------------------------
        public const String ServerName = "WiiTDB";
        public const String ServerUrl = "http://wiitdb.com/";

        public const String CoverAddr = ServerUrl + "wiitdb/artwork/";
        public static readonly String[] ModeAddr = new String[]
        { "cover/", "cover3D/", "disc/", "disccustom/" };

        public const String Folder = "Covers/";


        //---------------------------------------------------------------------------------------------------
        //
        //---------------------------------------------------------------------------------------------------
        static WebOperation CurrentOperation = null;


        //---------------------------------------------------------------------------------------------------
        //
        //---------------------------------------------------------------------------------------------------
        public static String LanguageCode
        {
            get
            {
                return CultureInfo.CurrentCulture.
                    Name.Substring(0, 2).ToUpper();
            }
        }


        //---------------------------------------------------------------------------------------------------
        //
        //---------------------------------------------------------------------------------------------------
        public static String BuildCode(String code, String region)
        {
            Char r;
            switch (region)
            {
                case "US": r = 'E'; break;
                case "JA": r = 'J'; break;
                case "KO": r = 'K'; break;
                case "ZH": r = 'W'; break;
                default: r = 'P'; break;
            }

            code = code.ToUpper();
            return code.Substring(0, 3) + r + code.Substring(4);
        }


        //---------------------------------------------------------------------------------------------------
        //
        //---------------------------------------------------------------------------------------------------
        public static String BuildFreeCode(String code)
        {
            // Yes, it's a really complicated algorithm...
            code = code.ToUpper();
            return code.Substring(0, 3) + '_' + code.Substring(4);
        }


        //---------------------------------------------------------------------------------------------------
        //
        //---------------------------------------------------------------------------------------------------
        public static ImageSource LoadCover(String code, OperationDelegate asyncResponse)
        {
            String dir = Folder + ModeAddr[(int)Config.CoverMode];
            String freecode = BuildFreeCode(code);

            String path = Path.GetFullPath(dir + code + ".png");
            String freepath = Path.GetFullPath(dir + freecode + ".png");

            if (File.Exists(path))
            {
                try
                {
                    // Custom cover
                    BitmapImage bmp = new BitmapImage();
                    bmp.BeginInit();
                    bmp.UriSource = new Uri(path);
                    bmp.CacheOption = BitmapCacheOption.OnLoad;
                    bmp.EndInit();
                    return bmp;
                }
                catch
                {
                    ImageSourceConverter converter = new ImageSourceConverter();
                    return (ImageSource)converter.ConvertFromString(
                        "pack://application:,,,/Icons/Browser/cover_error.PNG");
                }
            }
            else if (!File.Exists(freepath))
            {
                BeginDownload(new String[] { code }, asyncResponse);

                ImageSourceConverter converter = new ImageSourceConverter();
                return (ImageSource)converter.ConvertFromString(
                    "pack://application:,,,/Icons/Browser/cover_downloading.PNG");
            }
            else
            {
                try
                {
                    BitmapImage bmp = new BitmapImage();
                    bmp.BeginInit();
                    bmp.UriSource = new Uri(freepath);
                    bmp.CacheOption = BitmapCacheOption.OnLoad;
                    bmp.EndInit();

                    // Fix DPI
                    if (bmp.DpiX != 96)
                    {
                        int stride = bmp.PixelWidth * 4;
                        byte[] pixels = new byte[stride * bmp.PixelHeight];
                        bmp.CopyPixels(pixels, stride, 0);

                        BitmapSource fix = BitmapSource.Create(bmp.PixelWidth,
                            bmp.PixelHeight, 96, 96, PixelFormats.Bgra32,
                            null, pixels, stride);

                        FileStream fdest = File.Create(freepath);

                        PngBitmapEncoder enc = new PngBitmapEncoder();
                        enc.Frames.Add(BitmapFrame.Create(fix));
                        enc.Save(fdest);

                        fdest.Close();

                        return fix;
                    }
                    else
                    {
                        return bmp;
                    }
                }
                catch
                {
                    ImageSourceConverter converter = new ImageSourceConverter();
                    return (ImageSource)converter.ConvertFromString(
                        "pack://application:,,,/Icons/Browser/cover_error.PNG");
                }
            }
        }


        //---------------------------------------------------------------------------------------------------
        //
        //---------------------------------------------------------------------------------------------------
        public static WebOperation BeginDownload(String[] codes, OperationDelegate asyncResponse)
        {
            if (CurrentOperation != null)
            {
                CurrentOperation.Pause();

                var newFiles = from c in codes
                               where !CurrentOperation.Files.Contains(c)
                               select c;

                String[] temp = CurrentOperation.Files;
                CurrentOperation.Files = new String[temp.Length + newFiles.Count()];
                CurrentOperation.TotalCount = CurrentOperation.Files.Length;

                temp.CopyTo(CurrentOperation.Files, 0);
                newFiles.ToArray().CopyTo(CurrentOperation.Files, temp.Length);

                if (asyncResponse != null)
                    CurrentOperation.StepCompleted += asyncResponse;

                CurrentOperation.Resume();
            }
            else
            {
                WebOperation op = new WebOperation("Cover Download", OperationType.Cover);

                if (asyncResponse != null)
                    op.StepCompleted += asyncResponse;

                op.Files = new String[codes.Length];
                op.Dialog = new DialogCopyFiles();
                op.Dialog.Owner = Application.Current.Windows[0];
                op.Dialog.WindowStartupLocation = WindowStartupLocation.CenterOwner;

                op.StepUpdated += new OperationDelegate(UpdateDialogProgress);
                op.Dialog.CancelButton.Click += new RoutedEventHandler(CancelDownload);

                codes.CopyTo(op.Files, 0);

                CurrentOperation = op;
                Threads.LaunchOperation(op, new ParameterizedThreadStart(DownloadAsync));
            }

            UpdateDialogInfo(CurrentOperation, WbfsError.Ok, 0);
            return CurrentOperation;
        }


        //---------------------------------------------------------------------------------------------------
        //
        //---------------------------------------------------------------------------------------------------
        private static void DownloadAsync(Object o)
        {
            WebOperation op = o as WebOperation;

            WebClient client = new WebClient();
            String[] languages;
            String url;
            String dir;

            languages = new String[]
            {
                LanguageCode,
                "EN",
                "US",
            };

            url = CoverAddr + ModeAddr[(int)Config.CoverMode];
            dir = Folder + ModeAddr[(int)Config.CoverMode];

            if (!Directory.Exists(dir))
            {
                try { Directory.CreateDirectory(dir); }
                catch { op.Cancel(); return; }
            }

            //-----------------------------------------------------------

            op.TotalCount = op.Files.Length;
            op.Dialog.BeginSetProgress(1, 0);
            op.Dialog.BeginShow();

            //-----------------------------------------------------------

            for (int c = 0; c < op.Files.Length; c++)
            {
                if (op.IsPaused)
                {
                    op.Dialog.BeginSetProgress(-1, 0);

                    while (op.IsPaused)
                        Thread.Sleep(10);
                }

                //-------------------------------------------------------

                if (op.IsCanceled)
                {
                    op.Step(-1, WbfsError.Cancel);
                    goto END;
                }

                //-------------------------------------------------------

                WbfsError error = WbfsError.Ok;
                String code = op.Files[c];

                StringBuilder address =
                    new StringBuilder(512);

                String dest = Path.GetFullPath(dir +
                    BuildFreeCode(code) + ".png");

                for (int i = 0; i < languages.Length; i++)
                {
                    address.Clear();
                    address.AppendFormat("{0}{1}{2}{3}{4}", url, languages[i], 
                        '/', BuildCode(code, languages[i]), ".png");

                    //---------------------------------------------------

                    error = WbfsError.Ok;
                    op.CurrentCount = 1;

                    try
                    {
                        // Request the file size
                        HttpWebRequest request = (HttpWebRequest)WebRequest.Create(address.ToString());
                        request.Credentials = CredentialCache.DefaultCredentials;

                        HttpWebResponse response = (HttpWebResponse)request.GetResponse();
                        op.CurrentCount = response.ContentLength + 1;
                        op.CurrentNotifyStep = response.ContentLength / 100;
                        op.Current = 0;
                        response.Close();

                        // Download
                        Byte[] buffer = new Byte[4096];

                        Stream input = client.OpenRead(address.ToString());
                        Stream output = File.Open(dest, FileMode.Create);

                        int read;

                        while ((read = input.Read(buffer, 0, 4096)) != 0)
                        {
                            if (op.IsCanceled)
                                break;

                            output.Write(buffer, 0, read);
                            op.Step(read, WbfsError.Ok);
                        }

                        input.Close();
                        output.Close();

                        if (op.IsCanceled)
                        {
                            if (File.Exists(dest))
                                File.Delete(dest);

                            op.Step(-1, WbfsError.Cancel);

                            goto END;
                        }

                        // Fix DPI
                        BitmapImage bmp = new BitmapImage();
                        bmp.BeginInit();
                        bmp.UriSource = new Uri(dest);
                        bmp.CacheOption = BitmapCacheOption.OnLoad;
                        bmp.EndInit();

                        if (bmp.DpiX != 96)
                        {
                            int stride = bmp.PixelWidth * 4;
                            byte[] pixels = new byte[stride * bmp.PixelHeight];
                            bmp.CopyPixels(pixels, stride, 0);

                            BitmapSource fix = BitmapSource.Create(bmp.PixelWidth,
                                bmp.PixelHeight, 96, 96, PixelFormats.Bgra32,
                                null, pixels, stride);

                            FileStream fdest = File.Create(dest);

                            PngBitmapEncoder enc = new PngBitmapEncoder();
                            enc.Frames.Add(BitmapFrame.Create(fix));
                            enc.Save(fdest);

                            fdest.Close();
                        }
                        else
                        {
                            bmp = null;
                        }

                        error = WbfsError.Ok;
                        op.Step(1, WbfsError.Ok);

                    }
                    catch
                    {
                        error = WbfsError.Fail;
                        continue;
                    }

                    break;
                }

                if (error != WbfsError.Ok)
                {
                    op.Step(op.CurrentCount - op.Current, error);
                }
            }

        END:

            CurrentOperation = null;
            op.Dialog.BeginClose();
        }


        //---------------------------------------------------------------------------------------------------
        //
        //---------------------------------------------------------------------------------------------------
        private static void CancelDownload(object sender, RoutedEventArgs e)
        {
            if (CurrentOperation != null)
                CurrentOperation.Cancel();
        }


        //---------------------------------------------------------------------------------------------------
        //
        //---------------------------------------------------------------------------------------------------
        private static void UpdateDialogInfo(Operation sender, WbfsError error, int element)
        {
            WebOperation op = sender as WebOperation;

            CurrentOperation.Dialog.BeginSetBasicInfo(
                CurrentOperation.Files.Length > 1 ?
                "Downloading " + CurrentOperation.Files.Length +
                " covers" : "Downloading 1 cover", ServerName,
                "(" + ServerUrl + ")", "Covers", "");
        }


        //---------------------------------------------------------------------------------------------------
        //
        //---------------------------------------------------------------------------------------------------
        private static void UpdateDialogProgress(Operation sender, WbfsError error, int element)
        {
            WebOperation op = sender as WebOperation;

            op.TotalCount = op.Files.Length;
            op.Dialog.BeginSetProgress(op.TotalCount, op.IsPaused ? -1 : 
                op.Total + ((double)op.Current / (double)op.CurrentCount));
        }
    }
}
